package cn.gddevon.beans.factory.utils;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 类操作工具包
 */
public class ClazzUtils {
        private static final String CLASS_SUFFIX = ".class";
        private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
        private static final String PACKAGE_SEPARATOR = ".";


        /**
         * 查找包下的所有类的名字
         *
         * @param packageName
         * @param showChildPackageFlag 是否需要显示子包内容
         * @return List集合，内容为类的全名
         */
        public static List<String> getClazzName(String packageName, boolean showChildPackageFlag) {
            List<String> result = new ArrayList<>();
            // cn.gddevon => cn/gddevon
            String suffixPath = packageName.replaceAll("\\.", "/");
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            try {
                Enumeration<URL> urls = loader.getResources(suffixPath);
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    if (url != null) {
                        String protocol = url.getProtocol();
                        if ("file".equals(protocol)) {
                            // C:\Users\德文\Desktop\编程笔记\设计模式\spring_demo\target\classes\cn\gddevon\testurl
                            String path = url.toURI().getPath();
//                            System.out.println(path);
                            result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
                        }
                        /* 此处会将导入的依赖jar包进行扫描
                        //C:\Users\德文\.m2\repository\cn\gddevon\spring_framework\1.0-SNAPSHOT\spring_framework-1.0-SNAPSHOT.jar
                        else if ("jar".equals(protocol)) {
                            JarFile jarFile = null;
                            try {
                                jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                                System.out.println(jarFile.getName());
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (jarFile != null) {
                                result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
                            }
                        } */
                    }
                }
            } catch (IOException | URISyntaxException e) {
                e.printStackTrace();
            }
            return result;
        }


        /**
         * 递归获取所有class文件的名字
         *
         * @param file
         * @param flag 是否需要迭代遍历
         * @return List
         */
        private static List<String> getAllClassNameByFile(File file, boolean flag) {
            List<String> result = new ArrayList<>();
            if (!file.exists()) {
                return result;
            }
            if (file.isFile()) {
                String path = file.getPath();
                // 注意：这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
                if (path.endsWith(CLASS_SUFFIX)) {
                    path = path.replace(CLASS_SUFFIX, "");
                    // 从"/classes/"后面开始截取
                    String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
                            .replace(File.separator, PACKAGE_SEPARATOR);
                    if (-1 == clazzName.indexOf("$")) {
                        result.add(clazzName);
                    }
                }
                return result;

            } else {
                File[] listFiles = file.listFiles();
                if (listFiles != null && listFiles.length > 0) {
                    for (File f : listFiles) {
                        if (flag) {
                            result.addAll(getAllClassNameByFile(f, flag));
                        } else {
                            if (f.isFile()) {
                                String path = f.getPath();
                                if (path.endsWith(CLASS_SUFFIX)) {
                                    path = path.replace(CLASS_SUFFIX, "");
                                    // 从"/classes/"后面开始截取
                                    String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
                                            .replace(File.separator, PACKAGE_SEPARATOR);
                                    if (-1 == clazzName.indexOf("$")) {
                                        result.add(clazzName);
                                    }
                                }
                            }
                        }
                    }
                }
                return result;
            }
        }


        /**
         * 递归获取jar所有class文件的名字
         *
         * @param jarFile
         * @param packageName 包名
         * @param flag        是否需要迭代遍历
         * @return List
         */
        private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {
            List<String> result = new ArrayList<>();
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String name = jarEntry.getName();
                // 判断是不是class文件
                if (name.endsWith(CLASS_SUFFIX)) {
                    name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
                    if (flag) {
                        // 如果要子包的文件,那么就只要开头相同且不是内部类就ok
                        if (name.startsWith(packageName) && -1 == name.indexOf("$")) {
                            result.add(name);
                        }
                    } else {
                        // 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类
                        if (packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
                            result.add(name);
                        }
                    }
                }
            }
            return result;
        }
    }
